#ifndef DEFORMABLE_MESH_H
#define DEFORMABLE_MESH_H

#include "model_obj.h"
#include "vmatrix.h"
#include "vertexcluster.h"
#include "fpsmeasurer.h"

enum PlasticityMode
{
	PLASTICITY_MODE_NONE,
	PLASTICITY_MODE_UPDATE,
	PLASTICITY_MODE_REMODEL
};

enum DeformationMode
{
	DEFORMATION_MODE_LINEAR,
	DEFORMATION_MODE_QUADRATIC
};

enum VertexBlockMode
{
	VERTEX_BLOCK_MODE_FREE = 0,
	VERTEX_BLOCK_MODE_LOCK_X = 1,
	VERTEX_BLOCK_MODE_LOCK_Y = 2,
	VERTEX_BLOCK_MODE_LOCK_Z = 4,
	VERTEX_BLOCK_MODE_LOCK_ALL = 7
};

enum SimulationState 
{
	SIMULATION_STATE_STOPPED,
	SIMULATION_STATE_APPLYING_FORCES,
	SIMULATION_STATE_COLLISION_CONSTRAINTS,
	SIMULATION_STATE_DEFORMABLE_BODY_CONSTRAINTS,
	SIMULATION_STATE_INTEGRATION_FINISH
};

//class VertexConnection 
//{
//public:
//	int cluster1;
//	int cluster2;
//	Connection *connectionFrom1To2;
//	Connection *connectionFrom2To1;
//
//	bool isBetween( int cluster1, int cluster2 )
//	{
//		return ( this->cluster1 == cluster1 && this->cluster2 == cluster2 ) || ( this->cluster1 == cluster2 && this->cluster2 == cluster1 );
//	}
//};


class FractureMesh : public ModelOBJ
{
public:
	static const float FLOOR_HEIGHT;

	FractureMesh();
	FractureMesh( DeformationMode mode );
	//~FractureMesh();

	bool import( const char* fileName, int numberOfClusters, int primitives, float dx, float dy, float dz, float delta, DeformationMode deformationMode = DEFORMATION_MODE_LINEAR );

	bool importMarshmallow( const char* fileName, int numberOfClusters, float delta );

	int relaxation;
	//void createArcs();

	void update( float dt );

	void moveMesh( float dx, float dy );
	void moveMesh( float dx, float dy, int cluster );
	void moveMeshRotating( float cX, float xY );
	void moveMeshByForce( float dx, float dy, int cluster );

	void moveVertex( int vertexId, float dx, float dy, float dz );
	void displaceVertex( int i, float dX, float dY, float dZ );

	void rotateVertex( int vertexIndex, float angle );

	void setVertexBlockMode( int vertexIndex, VertexBlockMode blockMode );
	VertexBlockMode getVertexBlockMode( int vertexIndex );

	/*float getRigidity() const { return rigidity; }
	void setRigidity( float rigidity );*/
	float alpha;
	float beta;

	float sigma;

	float cYield;
	float cCreep;
	float cMax;

	PlasticityMode plasticityMode;
	//float getBeta();
	//float getAlpha();

	//PlasticityMode getPlasticityMode() const { return plasticityMode; }
	//void setPlasticityMode( PlasticityMode plasticityMode ) { this->plasticityMode = plasticityMode; }

	ModelOBJ::OBJVertex& getVertexForDrawing( int index ) 
	{
		if( simulationState == SIMULATION_STATE_DEFORMABLE_BODY_CONSTRAINTS || simulationState == SIMULATION_STATE_COLLISION_CONSTRAINTS )
			return intermediatePosition[ index ];
		else
			return getVertex( index );
	}

	inline void setSimulationStepByStep( bool isSimulationStepByStep )
	{
		this->isSimulationStepByStep = isSimulationStepByStep;
	}

	inline bool getSimulationStepByStep() const
	{
		return isSimulationStepByStep;
	}

	Color* getColorBuffer();
	ModelOBJ::OBJVertex* getVertexBufferForDrawing();

	bool getRecalculateWeights() const 
	{ 
		return recalculateWeights;
	}

	void setRecalculateWeights(bool recalculateWeights) 
	{ 
		this->recalculateWeights = recalculateWeights;

		for( int i = 0; i < numberOfClusters; i++ )
		{
			clusters[ i ]->setRecalculateWeights( recalculateWeights );
		}
	}

	void squish( float dy );

	int vertexCollisionCount( int vertexIndex );

	void stopAllVertices();

	void setFracturable( bool fracturable );
	bool getFracturable();

	bool checkAgainstCube;
	bool checkAgainstSphere;
	static float obstaclePosition[3];
	static float obstacleSpeed[3];

	void blockVerticesFromCluster( int cluster, bool block = true );
	void blockAllVerticesBelow( float y );
	void blockVerticesFromWindow( bool hard );
	
	bool fracturable;
	
	static float GRAVITY_ACC;

	bool isBaloon;

	bool isWindow;
	static float windowHardness;

protected:

	//static const int RELAXATION_MAX_ITERATIONS = 1;
	
	static const float RESTITUTION;
	//static const float ACCEPTABLE_DEEPNESS;
	//static const float ENERGY_PRESERVATION;
	static const float INTERPENETRATION_CORRECTION_FACTOR;

	//float rigidity

	DeformationMode deformationMode;
	
	bool isSimulationStepByStep;
	SimulationState simulationState;

	int numberOfClusters;

	float squishHeight;

	std::vector<VertexCluster*> clusters;

	// cluster id for each vertex
	std::vector<std::vector<std::pair<int,int>>> vertexClusters;

	PartitionGraph meshClusterGraph;

	// cache for total number of vertices of the model
	int numberOfVertices;

	std::vector<ModelOBJ::OBJVertex> vSpeed;

	std::vector<ModelOBJ::OBJVertex> intermediatePosition;
	std::vector<ModelOBJ::OBJVertex> intermediateSpeed;

	//std::map< int, MyClusterGraph > clusterGraphs;

	//std::vector<int> collisionCount;

	std::map< int, std::map< int, std::map< int, bool >>> edges;

	// says whether the vertex can be moved by external forces
	std::vector< VertexBlockMode > vertexBlockedStatus;

	// current bounce accumulated time
	//bool isBouncing;
	//float currentBouncingTime;
	
	bool weightHasChanged;
	bool recalculateWeights;
	//float deepness;
	bool shouldRecalculate;
	//float move[2];
	//float inline calculateRigidityFactor();
	bool* belongsToCluster;

	int lastPartitionCount;


	std::map< int, std::map< int, std::map< int, Connection* > > > vertexConnections;

	//void updatePhysicalSystem( float dt );
	//void updateDeformationSystem( float dt );
	void reset( DeformationMode mode );

	void calculateAlphaAndBeta();

	void applyForces( float dt );

	//void updateDeepness( float yPosition );

	void updateContinuous( float dt );

	void passToIntermediateValues();

	//void applyConstraints( float dt );
	void finishIntegration( float dt );

	//float getDeepness();

	void applyCollisionConstraints( float dt );

	void preparePositionAndSpeed();

	//float calculateNewVertexWeight( int vertexIndex  );

	void applyDeformableConstraints( float dt );
	//float getIntermediateDeepness();
	void loadingVertexWeightInformation( std::ifstream& fileHandle );
	
	//std::vector<ModelOBJ::OBJVertex> fractureSpeed;
	bool applyFractures();

	void recalculateVertexWeights( int vIndex, PartitionGraph& graph );

	//void startUpdate();
	void calculateVertexColor( int vertexIndex );
	std::map<int,bool> getEdgesBetweenClusters( int clusterIndex1, int clusterIndex2 );
	void breakEdge( int edgeIndex );
	OBJVertex& addVertexCopy( int vertexIndex, int* copyIndex );
	void removeFromVertexConnections( int i, int j, int vIndex, std::map<int,std::map<int,std::map<int,Connection*>>>& vertexConnections );

};
#endif

